#
# NoiseSpime - A Spime collecting raw noise data
# version 0.1
# last update 2008 06 26
#
# Copyright (C) 2008, licensed under GPL v3
# Babele Dunnit <Babele.Dunnit AT gmail DOT com>
# Roberto Ostinelli <roberto AT openspime DOT com>
#
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License v3 as published by
# the Free Software Foundation.
#
# You should have received a copy of the GNU General Public License v3
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Permission to use, copy, modify, and distribute this software and its
# documentation for any purpose with or without fee is hereby granted,
# provided that the above copyright notice and this permission notice
# appear in all copies.
# 
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
# ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY,
# OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE
# AUTHORS OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING
# FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE IMPLEMENTATION,
# DEPLOYMENT, OR OTHER USE OF THE SOFTWARE.
#
# IN NO EVENT AND UNDER NO LEGAL THEORY, WHETHER IN TORT (INCLUDING
# NEGLIGENCE), CONTRACT, OR OTHERWISE, UNLESS REQUIRED BY APPLICABLE LAW
# (SUCH AS DELIBERATE AND GROSSLY NEGLIGENT ACTS) OR AGREED TO IN WRITING,
# SHALL ANY AUTHOR OF THIS SOFTWARE BE LIABLE FOR DAMAGES,
# INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
# DAMAGES OF ANY CHARACTER ARISING OUT OF THE USE OR INABILITY TO USE THE
# SOFTWARE (INCLUDING BUT NOT LIMITED TO DAMAGES FOR LOSS OF GOODWILL, WORK
# STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL
# DAMAGES OR LOSSES), EVEN IF SUCH AUTHOR HAS BEEN ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGES.


"""Spime, normal code"""

###### Imports
import sys, os
os.chdir(os.path.abspath(os.path.dirname(sys.argv[0])))
sys.path.append('lib') # use the local library
from pyopenspime.core import Client
import pyopenspime.extension.datareporting
import time, array, math
import pyaudio

# Compute audio RMS   
def SampleAudio():
    # set format
    FORMAT = pyaudio.paInt16
    CHANNELS = 1
    RATE = 44100
    RECORD_SECONDS = 1
    CHUNK = RATE * RECORD_SECONDS
    # get stream
    p = pyaudio.PyAudio()
    stream = p.open(format = FORMAT,
        channels = CHANNELS, 
        rate = RATE, 
        input = True,
        output = True)
    data = stream.read(CHUNK)
    squaresSum = 0
    dataArray = array.array('h', data)
    for i in range(0, CHUNK):
        value = dataArray[i]
        squaresSum += math.pow(value, 2) 
    squaresSum /= float(CHUNK)
    stream.stop_stream()
    stream.close()
    p.terminate()
    rms = math.sqrt(squaresSum)
    rms = rms / 256
    log.debug(u'computed %s rms' % rms)
    # return computed rms
    return rms


###### Logging
import logging
logging.basicConfig(level = 10, format='%(asctime)s %(levelname)s %(message)s')
log = logging.getLogger("MySpime")

###### read latitude and longitude
lat = None
lon = None
loop = None
f = open('noisespime.ini', "r" )
for line in f:
    line = line.strip().replace('\r','').replace('\n','').replace(' ','').lower()
    if len(line) > 0 and line[:1] <> '#':
        l = line.split('=')
        if len(l) == 2:
            try:
                if l[0] == 'lat':
                    lat = float(l[1])
                if l[0] == 'lon':
                    lon = float(l[1])
                if l[0] == 'loop':
                    loop = int(l[1])
            except:
                pass
if lat == None or lon == None or loop == None:
    log.error(u'invalid noisespime.ini file')

###### OpenSpime Client
# Create new client -> bind log callback function
c = Client(osid_or_osid_path = 'developer@developer.openspime.com/spime', log_callback_function = log.log)

# Connect to OpenSpime SpimeGate
try:
    c.connect()
except:
    log.error("error (%s) while connecting: %s" % (sys.exc_info()[0].__name__, sys.exc_info()[1]))
    exit(1)


###### Callback functions
# define callbacks to data reporting
def on_success(stanza_id, stanza):
    log.info(u'data with id \'%s\' succesfully received by recipient.' % stanza_id)

def on_failure(stanza_id, error_cond, error_description, stanza):
    log.error(u"error (%s) on transmission of data with id \'%s\': %s" % (error_cond, stanza_id, error_description))

def on_timeout(stanza_id):
    log.error(u'timeout waiting confirmation for data with id \'%s\'.' % stanza_id)

# set handlers
c.set_iq_handlers(on_success, on_failure, on_timeout, 60)


# current time in international standard ISO 8601
def StrMinLen(str_num, str_len):
    while len(str_num) < str_len:
        str_num = "0%s" % str_num
    return str_num
def GetCurrentDateTime():
    if -time.timezone < 0:
        symbol = '-'
    else:
        symbol = '+'
    tz = "%s%s:%s" % ( symbol, StrMinLen(str(int(-time.timezone / 3600)), 2), StrMinLen(str(int((-time.timezone % 3600)/60)), 2) )
    return "%s%s" % (time.strftime("%Y-%m-%dT%H:%M:%S"), tz)


###### Send message
def SendMessage(measured_rms):
    # Create data reporting message which requests for confirmation (i.e. of type 'iq')
    dr = pyopenspime.extension.datareporting.ExtObj()
    # Add node
    dr.add_entry(u"""<entry>
            <date>%s</date>
            <lat>%s</lat>
            <lon>%s</lon>
            <rms>%s</rms>
        </entry>""" % (GetCurrentDateTime(), lat, lon, measured_rms ) )
    # build message of kind 'iq', i.e. will wait for a confirmation or error message.
    iq = dr.build('iq')
    # send
    log.info(u'sending data reporting message with id \'%s\'' % iq.getID())
    c.send_stanza(iq, 'scopenode@developer.openspime.com/noise', encrypt = True, sign = True)
    log.info(u'data reporting message queued for sending.')


###### Listening loop (client up)
l = 0
try:
    while c.loop(1):
        # loop counter
        l += 1
        if l > loop:
            # reset
            l = 0
            # compute and send
            SendMessage(SampleAudio())
            
except KeyboardInterrupt:
    log.info(u'disconnecting and exiting')
    if c.isConnected == True:
        c.disconnect()
    exit(0)
except:
    log.error( "error (%s) while looping: %s" % (sys.exc_info()[0].__name__, sys.exc_info()[1]))
    exit(2)


